package com.tqyan.service.chat;

import com.alibaba.fastjson.JSONObject;
import com.tqyan.dal.mapper.ChatContentMapper;
import com.tqyan.model.controller.request.OpenAIChatRequest;
import com.tqyan.model.controller.response.OpenAIChatResp;
import com.tqyan.model.dto.ChatContentDTO;
import com.tqyan.model.dto.ChatDTO;
import com.tqyan.model.dto.UserDTO;
import com.tqyan.model.dto.UserSettingDTO;
import com.tqyan.model.enums.*;
import com.tqyan.model.exp.DefaultException;
import com.tqyan.service.service.AccountService;
import com.tqyan.service.service.DataShadowService;
import com.tqyan.service.transfer.LimitService;
import com.tqyan.service.transfer.OpenAIService;
import com.tqyan.service.transfer.WordService;
import com.tqyan.util.DateUtil;
import com.tqyan.util.SnowflakeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 聊天指令抽象
 * <p> <功能详细描述> </p>
 *
 * @author 陈晨
 * @version 1.0
 * @date 2023/5/4
 */
@Slf4j
public abstract class AbstractChatPointService implements ChatPointService {

    @Autowired
    protected ChatContentMapper chatContentMapper;

    @Resource(name = "chatExecutor")
    protected Executor chatExecutor;
    @Autowired
    protected AccountService accountService;
    @Autowired
    protected OpenAIService openAIService;
    @Autowired
    protected LimitService limitService;
    @Autowired
    protected WordService wordService;
    @Autowired
    private DataShadowService dataShadowService;

    @Value("${service.url}")
    protected String fileUrl;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ChatContentDTO chat(UserDTO user, UserSettingDTO userSetting, ChatDTO chat, String content) {
        boolean isLock = this.lock(user.getOpenId(), chat.getId());
        if (!isLock) {
            throw new DefaultException("小燕子正在努力回答问题, 请耐心等待哦");
        }
        ChatContentDTO result = null;
        try {
            result = this.chatPoint(user, userSetting, chat, content);
            return result;
        } finally {
            this.unlock(user.getOpenId(), chat.getId(), result);
        }
    }

    /**
     * @description 加锁
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/5/5 18:26
     */
    protected boolean lock(String openId, long chatId) {
        return true;
    }

    /**
     * @description 解锁
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/5/5 18:26
     */
    protected void unlock(String openId, long chatId, ChatContentDTO result) {}

    /**
     * @description 聊天指令
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/5/4 19:40
     */
    protected abstract ChatContentDTO chatPoint(UserDTO user, UserSettingDTO userSetting, ChatDTO chat, String content);

    /**
     * @description 创建问题
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/5/4 16:20
     */
    protected ChatContentDTO createQuestion(UserDTO user, UserSettingDTO userSetting, ChatDTO chat, String content
            , String keywords, String answerContent) {
        ChatContentDTO question = new ChatContentDTO();
        question.setId(SnowflakeUtils.nextId());
        question.setUserId(user.getId());
        question.setChatId(chat.getId());
        question.setModel(userSetting.getModel());
        question.setTemperature(ChatCreativeAbilityEnum.get(userSetting.getCreativeAbility()).getTemperature());
        question.setRole(GPTRoleEnum.USER.getValue());
        question.setContent(content);
        question.setKeyword(keywords);
        question.setType(ChatContentTypeEnum.TEXT.getValue());
        question.setStatus(ChatContentStatusEnum.COMPLETED.getValue());
        question.setInputTokens(content.length());
        question.setGptModel(GPTModelEnum.GPT_3_5.getValue());
        if (StringUtils.isNotBlank(answerContent)) {
            question.setOutputTokens(answerContent.length());
        }
        boolean result = chatContentMapper.save(question) > 0;
        if (!result) {
            throw new DefaultException("问题入库失败");
        }
//        log.info("[聊天] openId={}, chatId={}, questionId={}, 问题入库完成", user.getOpenId(), chat.getId(), question.getId());
        return question;
    }

    /**
     * @description 创建回答
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/5/4 16:20
     */
    protected ChatContentDTO createAnswer(UserSettingDTO userSetting, ChatContentDTO question, String answerContent) {
        ChatContentDTO answer = new ChatContentDTO();
        answer.setId(SnowflakeUtils.nextId());
        answer.setUserId(0L);
        answer.setChatId(question.getChatId());
        answer.setModel(userSetting.getModel());
        answer.setTemperature(ChatCreativeAbilityEnum.get(userSetting.getCreativeAbility()).getTemperature());
        answer.setRole(GPTRoleEnum.ASSISTANT.getValue());
        answer.setType(ChatContentTypeEnum.TEXT.getValue());
        answer.setQuestionId(question.getId());
        if (StringUtils.isBlank(answerContent)) {
            answer.setContent("小燕子正在努力思考中");
            answer.setStatus(ChatContentStatusEnum.INCOMPLETE.getValue());
        } else {
            answer.setContent(answerContent);
            answer.setStatus(ChatContentStatusEnum.COMPLETED.getValue());
        }
        boolean result = chatContentMapper.save(answer) > 0;
        if (!result) {
            throw new DefaultException("答案入库失败");
        }
//        log.info("[聊天] openId={}, chatId={}, answer={}, 答案入库完成", user.getOpenId(), chat.getId(), answer.getId());
        return answer;
    }

    /**
     * @description 请求问题, 答案流响应
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/5/15 15:33
     */
    protected void chatExecute(UserDTO user, UserSettingDTO userSetting, ChatDTO chat
            , ChatContentDTO question, ChatContentDTO answer) {
        chatExecutor.execute(() -> {
            try {
                this.requestChatStream(user, userSetting, chat, question, answer);
            } catch (Exception e) {
                ChatContentDTO updateChatContent = new ChatContentDTO();
                updateChatContent.setId(answer.getId());
                updateChatContent.setChatId(answer.getChatId());
                updateChatContent.setContent("抱歉，远程服务器负载过高，暂时无法回答您的问题，您可以稍后尝试。");
                updateChatContent.setStatus(ChatContentStatusEnum.EXCEPTION.getValue());
                chatContentMapper.update(updateChatContent);
                log.error("[聊天] userId={}, chatId={}, questionId={}, answerId={}, GPT请求异常: {}"
                        , user.getId(), question.getChatId(), question.getId(), answer.getId()
                        , e.getMessage(), e);
            } finally {
                limitService.unlockChat(user.getOpenId(), question.getChatId());
            }
        });
    }

    /**
     * @description 请求问题, 答案流响应
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/4/13 19:43
     */
//    @Transactional(rollbackFor = Exception.class)
    private void requestChatStream(UserDTO user, UserSettingDTO userSetting, ChatDTO chat
            , ChatContentDTO question, ChatContentDTO answer) {
        // 请求GPT, 流响应
        long startTime = System.currentTimeMillis();
        OpenAIChatRequest chatRequest = this.generateChatRequest(userSetting, question);
        final boolean[] isException = {false};
        AtomicBoolean isComplete = new AtomicBoolean(false);
        StringBuilder dataSB = new StringBuilder();
        openAIService.requestChatStream(chatRequest, data -> {
//            log.info("[聊天] data={}", data);
            if (StringUtils.isBlank(data) || isComplete.get()) {
                return;
            }
            // 响应异常
            if (isException[0] || data.indexOf("data: ") != 0) {
                isException[0] = true;
                dataSB.append(data);
                return;
            }
            // data: {"id":"chatcmpl-74mZ08jrRkhoKm6VRDUnY5EUljK5H","object":"chat.completion.chunk","created":1681374346,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"role":"assistant"},"index":0,"finish_reason":null}]}
            // data: {"id":"chatcmpl-74mZ08jrRkhoKm6VRDUnY5EUljK5H","object":"chat.completion.chunk","created":1681374346,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"有"},"index":0,"finish_reason":null}]}
            // data: {"id":"chatcmpl-74mZ08jrRkhoKm6VRDUnY5EUljK5H","object":"chat.completion.chunk","created":1681374346,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}
            // data: [DONE]
            OpenAIChatResp chatResp = JSONObject.parseObject(data.substring(6), OpenAIChatResp.class);
            // 答案完成
            if ("stop".equals(chatResp.getChoices().get(0).getFinish_reason())) {
                isComplete.set(true);
//                ChatContentDTO updateChatContent = new ChatContentDTO();
//                updateChatContent.setId(answer.getId());
//                updateChatContent.setChatId(answer.getChatId());
//                updateChatContent.setStatus(ChatContentStatusEnum.COMPLETED.getValue());
//                updateChatContent.setGptCompleteTime(DateUtil.now());
                boolean result = this.requestChatStop(user, userSetting, chat, question, answer);
                log.info("[聊天] userId={}, chatId={}, answerId={}, 答案完成: {}"
                        , user.getId(), answer.getChatId(), answer.getId(), result);
                return;
            }
            // 更新答案
            if (StringUtils.isNotBlank(chatResp.getChoices().get(0).getDelta().getRole())) {
                // 更新角色、模型、gptId
                ChatContentDTO updateChatContent = new ChatContentDTO();
                updateChatContent.setId(answer.getId());
                updateChatContent.setChatId(answer.getChatId());
                updateChatContent.setRole(chatResp.getChoices().get(0).getDelta().getRole());
                updateChatContent.setGptModel(chatResp.getModel());
                updateChatContent.setGptContentId(chatResp.getId());
                updateChatContent.setGptResponseTime(DateUtil.now());
                updateChatContent.setStatus(ChatContentStatusEnum.INCOMPLETE.getValue());
                if (!answer.getContent().contains(ChatPointContent.NOVEL_SPLIT)) {
                    updateChatContent.setContent("");
                } else {
                    updateChatContent.setContent(answer.getContent());
                }
                boolean result = chatContentMapper.update(updateChatContent) > 0;
                log.info("[聊天] gptId={}, answerId={}, 更新角色、模型、gptId: {}", chatResp.getId(), answer.getId(), result);
            } else {
                // 追加内容
                String appendContent = chatResp.getChoices().get(0).getDelta().getContent();
                chatContentMapper.appendContentByContentId(answer.getChatId(), answer.getId(), appendContent);
//                log.info("[聊天] gptId={}, answerId={}, appendContent={}, 追加内容", chatResp.getId(), answer.getId(), appendContent);
            }
        });
        long endTime = System.currentTimeMillis();
        ChatContentDTO completeAnswer = chatContentMapper.queryById(user.getId(), answer.getChatId(), answer.getId());
        log.info("[聊天] userId={}, chatId={}, answer={}, 请求GPT, 耗时: {}ms"
                , user.getId(), question.getChatId(), completeAnswer.getContent(), endTime - startTime);
        // GPT请求异常
        if (isException[0]) {
            ChatContentDTO updateChatContent = new ChatContentDTO();
            updateChatContent.setId(answer.getId());
            updateChatContent.setChatId(answer.getChatId());
            updateChatContent.setContent("抱歉，远程服务器负载过高，暂时无法回答您的问题，您可以稍后尝试。");
            updateChatContent.setStatus(ChatContentStatusEnum.EXCEPTION.getValue());
            chatContentMapper.update(updateChatContent);
            log.error("[聊天] userId={}, chatId={}, questionId={}, answerId={}, GPT请求异常: {}"
                    , user.getId(), question.getChatId(), question.getId(), answer.getId(), dataSB);
            return;
        }
        // 聊天请求完成, 回调
        this.requestChatComplete(user, userSetting, chat, question, completeAnswer);
    }

    /**
     * @description 聊天响应停止
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/5/15 18:24
     */
    protected boolean requestChatStop(UserDTO user, UserSettingDTO userSetting, ChatDTO chat
            , ChatContentDTO question, ChatContentDTO answer) {
        ChatContentDTO updateChatContent = new ChatContentDTO();
        updateChatContent.setId(answer.getId());
        updateChatContent.setChatId(answer.getChatId());
        updateChatContent.setStatus(ChatContentStatusEnum.COMPLETED.getValue());
        updateChatContent.setGptCompleteTime(DateUtil.now());
        return chatContentMapper.update(updateChatContent) > 0;
    }

    /**
     * @description 聊天请求完成
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/5/15 18:24
     */
    protected void requestChatComplete(UserDTO user, UserSettingDTO userSetting, ChatDTO chat
                , ChatContentDTO question, ChatContentDTO completeAnswer) {
    }

    /**
     * @description 构建聊天请求
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/4/12 22:37
     */
    protected OpenAIChatRequest generateChatRequest(UserSettingDTO userSetting, ChatContentDTO question) {
        return null;
    }

    /**
     * @description 追加数字缩影
     * <p> <功能详细描述> </p>
     *
     * @author 陈晨
     * @date 2023/4/23 14:50
     */
    protected void appendDataShadow(ChatContentDTO question, OpenAIChatRequest chatRequest) {
        if (StringUtils.isBlank(question.getKeyword())) {
            return;
        }
        boolean isAppend = dataShadowService.appendDataShadow(List.of(question.getKeyword().split(",")), chatRequest);
        if (!isAppend) {
            log.info("[聊天] keyword={}, 未匹配到合适的数字缩影", question.getKeyword());
        }
    }

}


